package ferp.core.calc.scorer;

import ferp.core.calc.Calculator;
import ferp.core.game.Bid;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.player.Player;

/**
 * User: olegs
 * Date: 12/3/13 2:19 PM
 */
public class Tricking extends Scorer
{
  public static final Tricking instance = new Tricking();

  @Override
  protected void update(Game g, Settings settings, Calculator c)
  {
    Player p2 = g.next(g.declarer());
    Player p3 = g.next(p2);
    int contract = g.contract();
    int tricks2 = g.getTricks(p2.id());
    int tricks3 = g.getTricks(p3.id());
    int whists = tricks2 + tricks3;

    // declarer managed to take tricks by  contract (at least)
    if (g.getDeclarerTricks() >= contract)
    {
      int eow = Calculator.getExpectedOpenWhists(contract);

      // write score to declarer's pool
      c.writeToPool(g, settings, g.declarer(), c.getPlayCostSuccess(contract));

      // both whisters are managed to take a minimum required whists
      if (whists >= eow)
        if (g.whisters == 2)
        {
          c.writeToWhists(g, p2, g.declarer(), c.getWhistCostSuccess(contract) * tricks2);
          c.writeToWhists(g, p3, g.declarer(), c.getWhistCostSuccess(contract) * tricks3);
        }
        else
          c.writeToWhists(g, g.opponent(), g.declarer(), c.getWhistCostSuccess(contract) * whists);
      else // one (or both whisters) failed to take all whists
        if (g.whisters == 2)
        {
          int esw = Calculator.getExpectedStandingWhists(contract);
          int missedWhists2 = getMissedWhists(esw, tricks2, tricks3);
          int missedWhists3 = getMissedWhists(esw, tricks3, tricks2);

          c.writeToWhists(g, p2, g.declarer(), c.getWhistCostSuccess(contract) * tricks2);
          c.writeToWhists(g, p3, g.declarer(), c.getWhistCostSuccess(contract) * tricks3);

          // write to mountain of the first whister only if contact is less than 8,
          // otherwise only second whister has to be punished
          if (missedWhists2 > 0 && contract < 8)
            c.writeToMountain(g, p2, c.getWhistCostFailure(contract) * missedWhists2);

          if (missedWhists3 > 0)
            c.writeToMountain(g, p3, c.getWhistCostFailure(contract) * missedWhists3);
        }
        else
        {
          c.writeToWhists(g, g.opponent(), g.declarer(), c.getWhistCostSuccess(contract) * whists);
          c.writeToMountain(g, g.opponent(), c.getWhistCostFailure(contract) * (eow - whists));
        }
    }
    else
    {
      // declarer failed to take tricks by contract
      int missed = contract - g.getDeclarerTricks();

      c.writeToMountain(g, g.declarer(), c.getPlayCostFailure(contract) * missed);

      // both said whist
      if (g.whisters == 2)
      {
        c.writeToWhists(g, p2, g.declarer(), c.getWhistCostSuccess(contract) * tricks2 + c.getMissedTrickCost(contract) * missed);
        c.writeToWhists(g, p3, g.declarer(), c.getWhistCostSuccess(contract) * tricks3 + c.getMissedTrickCost(contract) * missed);
      }
      else
        distributeWhists(g, settings, c, g.opponent(), g.passer(), contract, missed);
    }
  }

  @Override
  protected void statistics(Game g)
  {
    int contract = g.contract();
    Player p2 = g.next(g.declarer());
    Player p3 = g.next(p2);

    g.statistics.updateTricking(g.declarer().id(), contract, g.getDeclarerTricks());

    if (g.getBid(p2.id()).current.type == Bid.Type.WHIST)
      g.statistics.whists[p2.id()].sets++;
    else
      g.statistics.passes[p2.id()]++;

    if (g.getBid(p3.id()).current.type == Bid.Type.WHIST)
      g.statistics.whists[p3.id()].sets++;
    else
      g.statistics.passes[p3.id()]++;

    // declarer managed to take all tricks (at least)
    if (g.getDeclarerTricks() >= contract)
    {
      int tricks2 = g.getTricks(p2.id());
      int tricks3 = g.getTricks(p3.id());
      int eow = Calculator.getExpectedOpenWhists(contract);

      // both whisters are managed to take a minimum required whists
      if (tricks2 + tricks3 >= eow)
        if (g.whisters == 2)
        {
          g.statistics.whists[p2.id()].success++;
          g.statistics.whists[p3.id()].success++;
        }
        else
          g.statistics.whists[g.opponent().id()].success++;
      else // one (or both whisters) failed to take all whists
        if (g.whisters == 2)
        {
          int esw = Calculator.getExpectedStandingWhists(contract);
          int missedWhists2 = getMissedWhists(esw, tricks2, tricks3);
          int missedWhists3 = getMissedWhists(esw, tricks3, tricks2);

          if (missedWhists2 == 0 || contract >= 8)
            g.statistics.whists[p2.id()].success++;

          if (missedWhists3 == 0)
            g.statistics.whists[p3.id()].success++;
        }
    }
    else
      if (g.whisters == 2)
      {
        g.statistics.whists[p2.id()].success++;
        g.statistics.whists[p3.id()].success++;
      }
      else
        g.statistics.whists[g.opponent().id()].success++;
  }

  // called when both defenders play whists - the "whists" parameter is a number of whists of the player the method is called for
  private static int getMissedWhists(int esw, int whists, int partnerWhists)
  {
    if (whists >= esw)
      return 0;

    return partnerWhists <= esw ? esw - whists : esw - whists - (partnerWhists - esw);
  }

  // called when only one player said whist (no matter if playing open or standing game)
  private static void distributeWhists(Game g, Settings settings, Calculator c, Player whister, Player passer, int contract, int missed)
  {
    switch (settings.whistType)
    {
      case GREEDY:
        c.writeToWhists(g, whister, g.declarer(), c.getWhistCostSuccess(contract) * (g.getTricks(whister.id()) + g.getTricks(passer.id())) + c.getMissedTrickCost(contract) * missed);
        c.writeToWhists(g, passer, g.declarer(), c.getMissedTrickCost(contract) * missed);
        break;

      case GENTLEMAN:
        int whists = c.getWhistCostSuccess(contract) * (g.getTricks(whister.id()) + g.getTricks(passer.id())) + 2 * c.getMissedTrickCost(contract) * missed;

        c.writeToWhists(g, whister, g.declarer(), whists / 2);
        c.writeToWhists(g, passer, g.declarer(), whists / 2);
        break;
    }
  }
}
